////////////////////////////////////////////////////////////////////////////////////
//
// file : ProcMain.cs
//
// proj : Tring
// auth : instil
// date : 111023
// desc : Main game procedure
//
////////////////////////////////////////////////////////////////////////////////////

using UnityEngine;
using System.Collections;

public class ProcMain : MonoBehaviour {

    enum GameState
    {
        Start,
        Play,
        Over,
        Retry,
        Main
    }

    private Joystick joystick;

    public GameObject rock;
    public ParticleEmitter particleExplosion;
    public ParticleEmitter particleLeft;
    public ParticleEmitter particleRight;
    public AudioSource audioExplosion;
    public AudioSource audioBeep;
    public AudioSource audioTransition;
    public GUIText textScore;
    public GUITexture textureTrans;

    // Game
    private float fGameSpeed = 3.0f;
    private GameState g_eGameState = GameState.Start;
    private float g_fScore = 0.0f;
    private float g_fDistance = 0.0f;
    private float g_fTimer = 0.0f;
    private int g_nRocketPos = 0;
    private static bool g_bReverseButton = false;
    private float g_fRotationSpeed = 2.0f;

    private float g_fFPS = 0.0f;

    //public static bool IsReverseButton
    //{
    //    get { return g_bReverseButton; }
    //    set { g_bReverseButton = value; }
    //}

    // Rock
    private float g_fRockInterval = 10.0f;
    private const float ROCK_INTERVAL_MIN = 6.0f;
    private const float ROCK_SPEED_INCREASE = 0.2f;
    private const float ROCK_INITIAL_SPEED = -4.0f;
    public int nNumberOfRocks = 10;
    public float fRockGenerationHeight = 45.0f;
    public float fRockGenerationRadius = 5.0f;
    public float g_fRockSpeed = ROCK_INITIAL_SPEED;
    private int g_nPrevRoad = 0;
    private bool g_bRoad = false;

    // Rocket Rotation
    private const float ROCKET_ROTATION_SPEED = 180.0f * 0.75f;
    private const float ROCKET_ROTATION_SPEED_MAX = 180.0f * 1.25f;
    private const float ROCKET_ROLLING_SPEED = 10.0f;
    private const float ROCKET_ROLLING_RESTORE = ROCKET_ROLLING_SPEED * 3.0f;
    private const float ROCKET_ROLLING_BOOST_TIME = 0.3f;
    private float g_fDestinationAngle = 0.0f;
    private float g_fRotationAngle = 0.0f;
    private float g_fRocketRotationSpeed = ROCKET_ROTATION_SPEED;
    private float g_fThreashHoldAngle = 5.0f;
    private float g_fIntervalAngle;
    private bool g_bRotation = true;
    private float g_fRolling = 0.0f;

    private ArrayList g_vecRockPositions;
    private ArrayList g_vecRocketPositions;

    // GUI
    static private string g_strName = "Rocket";
    private Rect g_rtWindow = new Rect( 200.0f, 100.0f, 400.0f, 300.0f );
    private Vector2 scrollPosition = Vector2.zero;
    private float g_fWindowWidth = 300.0f;
    private float g_fWindowHeight = 420.0f;

    // Rank
    private const string g_strURL = "http://desti.ruree.net/node/rocket/";
    private LitJson.JsonData g_dataRank;

    private Rect m_rtExit = new Rect(0, 0, 100, 50);
    private Rect m_rtRetry = new Rect(0, 50, 100, 50);

    void OnGUI()
    {
        if (GUI.Button(m_rtExit, string.Format("{0:0.0}", g_fFPS)))
            Application.Quit();

        if( GUI.Button(m_rtRetry, "Retry" ))
            Application.LoadLevel(0);

    }

	// Use this for initialization
	void Start () {
        joystick = (Joystick)FindObjectOfType(typeof(Joystick));

        g_fIntervalAngle = 360.0f / nNumberOfRocks;

        g_vecRockPositions = new ArrayList( nNumberOfRocks );
        g_vecRocketPositions = new ArrayList( nNumberOfRocks );

        // Transition
        textureTrans.color = new Color32(255, 255, 255, 255);
    	
	    // Calculate rock position
        float fAngle;
        int i = 0;
        Vector3 pos;
	    for( ; i < nNumberOfRocks ; i++ )
	    {
		    fAngle = i * g_fIntervalAngle;
            pos = new Vector3( Mathf.Cos(fAngle * Mathf.Deg2Rad), 0.0f, Mathf.Sin(fAngle * Mathf.Deg2Rad) ) * fRockGenerationRadius;
            pos.y = fRockGenerationHeight;
            g_vecRockPositions.Add( pos );
	    }
    	
	    // Calculate Rocket position
	    for( i = 0 ; i < 360 ; i++ )
	    {
            g_vecRocketPositions.Add( new Vector3( Mathf.Cos(i * Mathf.Deg2Rad), 0.0f, Mathf.Sin(i * Mathf.Deg2Rad) ) * fRockGenerationRadius );
	    }
    	
        // Rocket Rotation
        RotateRocket();
        
        // GUI
        Vector2 vecScreenCenter = new Vector2(Screen.width * 0.5f, Screen.height * 0.5f);
        g_rtWindow = new Rect( vecScreenCenter.x - g_fWindowWidth * 0.5f,
					        vecScreenCenter.y - g_fWindowHeight * 0.5f,
					        g_fWindowWidth, g_fWindowHeight );
    					    
	    // Rank
	    UpdateRank();
	}
	
	// Update is called once per frame
    void Update()
    {
        Color32 color = textureTrans.color;
        switch (g_eGameState)
        {
            case GameState.Start:
                // Transition animation
                int preAlpha = color.a;
                color.a -= (byte)(Time.deltaTime * 255);

                textureTrans.color = color;

                if (preAlpha < color.a)
                {
                    color.a = 0;
                    textureTrans.color = color;
                    g_eGameState = GameState.Play;
                }
                break;
            case GameState.Play:
                // Process Input
                ProcessInput();

                // Rocket Rotation
                //RotateRocket();

                // Generate Rocks
                if (g_fTimer <= 0.0f)
                {
                    GenerateRock();
                    g_fTimer = fGameSpeed;
                }

                // Calculate score
                g_fScore += Time.deltaTime * g_fRockSpeed;

                // Balancing
                if (g_fRockInterval > ROCK_INTERVAL_MIN)
                    g_fRockInterval -= Time.deltaTime * 0.25f;

                textScore.text = string.Format("{0:0.0}km", -g_fScore);
                break;
            case GameState.Over:
                // Process Input
                //ProcessInput();
                break;
            case GameState.Retry:
                // Transition animation
                color.a -= (byte)(Time.deltaTime * 255);
                color.r += (byte)(Time.deltaTime * 255);
                color.g += (byte)(Time.deltaTime * 255);
                color.b += (byte)(Time.deltaTime * 255);
                textureTrans.color = color;

                if (textureTrans.color.a >= 1.0f)
                    Application.LoadLevel("GameIntro");
                break;
            case GameState.Main:
                // Transition animation
                color.a += (byte)(Time.deltaTime * 255);
                textureTrans.color = color;

                if (textureTrans.color.a >= 1.0f)
                    Application.LoadLevel("GameStart");
                break;
        }

        g_fTimer -= Time.deltaTime;

        g_fFPS = 1 / Time.deltaTime;
	}

    // Generate Rocks
    private void GenerateRock()
    {
	    GameObject rockClone;
	    int nDirection = (Random.value * 2.0f) < 1 ? -1 : 1;
	    int nNextRoad = g_nPrevRoad + nDirection;
    	
	    // Generate road
	    if( g_bRoad )
	    {
		    if( nNextRoad < 0 )
			    nNextRoad = nNumberOfRocks - 1;
		    else if( nNextRoad == nNumberOfRocks )
			    nNextRoad = 0;
    			
		    g_bRoad = false;
	    }
	    else
	    {
		    nNextRoad = g_nPrevRoad;
		    g_bRoad = true;
	    }
    	
	    // Generate rocks
	    int i = 0;
	    for( ; i < nNumberOfRocks ; i++ )
	    {
		    if( i == g_nPrevRoad || i == nNextRoad )
			    continue;
    			
		    //rockClone = (GameObject)Instantiate(rock, (Vector3)g_vecRockPositions[i], transform.rotation);
            rockClone = ProcRockPool.Instance.GetRock((Vector3)g_vecRockPositions[i], transform.rotation);
		    rockClone.rigidbody.velocity = new Vector3( 0.0f, g_fRockSpeed, 0.0f );
	    }
    	
	    // Remember current road
	    g_nPrevRoad = nNextRoad;
    	
	    // Calculate Rock speed and next generating time
	    g_fRockSpeed -= ROCK_SPEED_INCREASE;
	    fGameSpeed =  g_fRockInterval / -g_fRockSpeed;
    	
	    // Calculate Rocket rotation speed
	    g_fRocketRotationSpeed = ROCKET_ROTATION_SPEED * Mathf.Sqrt(g_fRockSpeed / ROCK_INITIAL_SPEED);
	    if( g_fRocketRotationSpeed > ROCKET_ROTATION_SPEED_MAX )
		    g_fRocketRotationSpeed = ROCKET_ROTATION_SPEED_MAX;
    }

    private bool leftBefore = false;
    private bool rightBefore = false;

    // Process Input
    private void ProcessInput()
    {
#if (UNITY_ANDROID || UNITY_IPHONE)// && (!UNITY_EDITOR)
        
	    switch( g_eGameState )
	    {
	    case GameState.Play:
            if (joystick.IsFingerDown())
            {
                Rotation(joystick.Position().x);
                //print(joystick.Position());
	        }
		    break;
	    }
#else
        switch ( g_eGameState )
	    {
	    case GameState.Play:
		    // Move Left
	        //if (Input.GetButtonDown("Fire1") || Input.GetKeyDown(KeyCode.LeftArrow) )
            if (Input.GetButton("Fire1") || Input.GetKey(KeyCode.LeftArrow))
            {
	            Rotation( !g_bReverseButton ? false : true);
	        }
    	    
	        // Move Right
	        //if (Input.GetButtonDown("Fire2") || Input.GetKeyDown(KeyCode.RightArrow) )
            if (Input.GetButton("Fire2") || Input.GetKey(KeyCode.RightArrow))
            {
	            Rotation( !g_bReverseButton ? true : false);
	        }
		    break;
	    }
#endif
    }

    private void Rotation( float fDelta )
    {
        g_fRotationAngle -= Time.deltaTime * g_fRotationSpeed * fDelta;

        //Debug.Log(g_fRotationAngle.ToString());

        // Rotate Rocket
        Quaternion qRot = Quaternion.identity;
        qRot = Quaternion.Euler(0, g_fRotationAngle, 0.0f);
        transform.rotation = qRot;

        // Set Rocket position
        Vector3 vPos = transform.position;
        vPos.x = Mathf.Cos(g_fRotationAngle) * fRockGenerationRadius;
        vPos.z = Mathf.Sin(g_fRotationAngle) * fRockGenerationRadius;

        transform.position = vPos;

        // Set camera look at
        vPos.y += 3.0f;
        Camera.mainCamera.transform.LookAt(vPos);
    }

    private void Rotation(bool bIsRight)
    {
        if (bIsRight)
            g_fRotationAngle -= Time.deltaTime * g_fRotationSpeed;
        else
            g_fRotationAngle += Time.deltaTime * g_fRotationSpeed;

        //Debug.Log(g_fRotationAngle.ToString());

        // Rotate Rocket
        Quaternion qRot = Quaternion.identity;
        qRot = Quaternion.Euler(0, g_fRotationAngle, 0.0f);
        transform.rotation = qRot;

        // Set Rocket position
        Vector3 vPos = transform.position;
        vPos.x = Mathf.Cos(g_fRotationAngle) * fRockGenerationRadius;
        vPos.z = Mathf.Sin(g_fRotationAngle) * fRockGenerationRadius;

        transform.position = vPos;

        // Set camera look at
        vPos.y += 3.0f;
        Camera.mainCamera.transform.LookAt(vPos);
    }

    private void Rotation2(bool bIsRight)
    {
        // Play rotation beep sound
        audioBeep.Play();

        // Rocket will be rotated
        g_bRotation = true;

        // Was right button clicked?
        if (bIsRight)
        {
            g_nRocketPos--;
            if (g_nRocketPos < 0)
                g_nRocketPos = nNumberOfRocks - 1;

            g_fRolling = -ROCKET_ROLLING_SPEED;

            // Emit Left Booster Particle
            FireParticle(particleLeft, ROCKET_ROLLING_BOOST_TIME);
        }
        else
        {
            g_nRocketPos++;
            if (g_nRocketPos >= nNumberOfRocks)
                g_nRocketPos = g_nRocketPos % nNumberOfRocks;

            g_fRolling = ROCKET_ROLLING_SPEED;

            // Emit Right Booster Particle
            FireParticle(particleRight, ROCKET_ROLLING_BOOST_TIME);
        }

        float fDeltaAngle = 360.0f / nNumberOfRocks;
        g_fDestinationAngle = g_nRocketPos * fDeltaAngle;

        Debug.Log("nRocketPos = " + g_nRocketPos + " Dest = " + g_fDestinationAngle + " Source = " + g_fRotationAngle);
    }

    // Rotation Rocket every tick when rotation flag is on
    private void RotateRocket()
    {
        // Restore Rocket roll
        if (g_fRolling > 1.0f)
            g_fRolling -= ROCKET_ROLLING_RESTORE * Time.deltaTime;
        else if (g_fRolling < -1.0f)
            g_fRolling += ROCKET_ROLLING_RESTORE * Time.deltaTime;
        else
            g_fRolling = 0.0f;

        // End of rotation
        if (g_bRotation == false)
            return;

        float fDeltaAngle;
        // Calculate rotation angle	
        if (g_fThreashHoldAngle < Mathf.Abs(g_fDestinationAngle - g_fRotationAngle))
        {
            if (g_fDestinationAngle < g_fRotationAngle)
            {
                if (g_fRotationAngle - g_fDestinationAngle > 180.0f)
                    fDeltaAngle = g_fRocketRotationSpeed;
                else
                    fDeltaAngle = -g_fRocketRotationSpeed;

                g_fRotationAngle += fDeltaAngle * Time.deltaTime;

                if (g_fDestinationAngle > g_fRotationAngle)
                    g_fRotationAngle = g_fDestinationAngle;
            }
            else
            {
                if (g_fDestinationAngle - g_fRotationAngle > 180.0f)
                    fDeltaAngle = -g_fRocketRotationSpeed;
                else
                    fDeltaAngle = g_fRocketRotationSpeed;

                g_fRotationAngle += fDeltaAngle * Time.deltaTime;

                if (g_fDestinationAngle < g_fRotationAngle)
                    g_fRotationAngle = g_fDestinationAngle;
            }

            if (g_fRotationAngle >= 360.0f)
                g_fRotationAngle -= 360.0f;
            else if (g_fRotationAngle < 0.0f)
                g_fRotationAngle = 360.0f + g_fRotationAngle;
        }
        else
        {
            g_fRotationAngle = g_fDestinationAngle;
            g_bRotation = false;
            g_fRolling = 0.0f;
        }

        // Rotate Rocket
        Quaternion qRot = Quaternion.identity;
        qRot = Quaternion.Euler(0, g_fRolling - g_fRotationAngle, 0.0f);
        transform.rotation = qRot;

        // Set Rocket position
        int n = (int)g_fRotationAngle;
        Vector3 vPos = (Vector3)g_vecRocketPositions[n];

        transform.position = vPos;

        // Set camera look at
        vPos.y += 3.0f;
        Camera.mainCamera.transform.LookAt(vPos);
    }

    // When Rocket and Rock was collided
    private IEnumerator OnCollisionEnter( Collision collision )
    {
	    if( g_eGameState == GameState.Retry || g_eGameState == GameState.Main )
		    yield return new WaitForEndOfFrame();

	    UpdateRank();
    	
	    // Play collision sound
	    audioExplosion.Play();
    	
	    // Emit explosion particle
	    FireParticle( particleExplosion, 0.5f );
    	
	    // Game Over
	    g_eGameState = GameState.Over;
    	
	    yield return new WaitForSeconds( 2.0f );
    		
    }

    // Fire particle in limited time
    private IEnumerator FireParticle( ParticleEmitter particle, float time )
    {
	    particle.emit = true;
	    yield return new WaitForSeconds(time);
	    particle.emit = false;
    }

    private void DoWindow( int windowID )
    {
        GUILayout.BeginVertical();
        
            GUILayout.BeginVertical(GUI.skin.GetStyle("box"));
            InsertListItem( "Rank", "Name", "Score" );
        	    if( g_dataRank != null )
        	    {
	        	    float fScore;
                    int i = 0;
		            for ( ; i < g_dataRank.Count; i++)
		            {
		        	    float.TryParse( g_dataRank[i]["score"].ToString(), out fScore );
		        	    InsertListItem( i+1, g_dataRank[i]["name"], string.Format("{0:0.0}km", fScore * 0.1f));
		            }
		            for ( ; i < 10 ; i++)
		            {
		        	    InsertListItem( i+1, "", "");
		            }
			    }
            GUILayout.EndVertical();
            
            GUILayout.BeginVertical(GUI.skin.GetStyle("box"));
		        GUILayout.BeginHorizontal();
	    	    GUILayout.Label("Score", GUILayout.Width(50));
		        GUILayout.Label(string.Format("{0:0.0}km", -g_fScore));
		        GUILayout.EndHorizontal();
		        GUILayout.BeginHorizontal();
	    	    GUILayout.Label("Name", GUILayout.Width(50));
		        g_strName = GUILayout.TextField(g_strName);
		        GUILayout.EndHorizontal();
		        if( GUILayout.Button("Retry") )
			    {
				    SendRank();
		            g_eGameState = GameState.Retry;
		            audioTransition.Play();
		        }
		        if( GUILayout.Button("Main Menu") )
			    {
				    SendRank();
		            g_eGameState = GameState.Main;
		            audioTransition.Play();
		        }
            GUILayout.EndVertical();
            
        GUILayout.EndVertical();
    }

    private IEnumerator UpdateRank()
    {
	    WWW www = new WWW( g_strURL + "JSON" );
	    yield return www;
    	
	    g_dataRank = LitJson.JsonMapper.ToObject( www.text );
    }

    private void InsertListItem( object rank, object name, object score )
    {
        GUILayout.BeginHorizontal();
        GUILayout.Label(rank.ToString(), GUILayout.Width(50));
        GUILayout.Label(name.ToString(), GUILayout.Width(100));
        GUILayout.Label(score.ToString(), GUILayout.Width(100));
        GUILayout.EndHorizontal();
    }

    private IEnumerator SendRank()
    {
	    WWWForm form = new WWWForm();
	    form.AddField("name", g_strName);
	    form.AddField("score", (int)(-g_fScore * 10.0f) );
    	
	    WWW www = new WWW( g_strURL, form );
    	
	    yield return www;
    	
	    UpdateRank();
    }

}